iT邦幫忙

2023 iThome 鐵人賽

DAY 20
0
  1. 開始之前我們調整一下之前在adapter\google\oauth.goOAuthLoginURL(),把原本寫死的第一個參數”state-token”改成外部代入的lineID,這個lineID可以在驗證完成後被帶到api/v1/ouath-login,也就是在處理Redirect URL時跟OAuth token一起取出。

    // internal\adapter\google\oauth.go
    func (oa *GoogleOAuth) OAuthLoginURL(lineID string) (oauthURL string) {
    	oauthURL = oa.Config.AuthCodeURL(lineID, oauth2.AccessTypeOffline, oauth2.ApprovalForce) // oauth2.ApprovalForce
    	return oauthURL
    }
    
  2. adapter的interface也要記得更新

    // internal\adapter\google\interface.go
    type GoogleOAuthI interface {
    	OAuthLoginURL(lineID string) (oauthURL string)
    	UserOAuthToken(authCode string) (*oauth2.Token, error)
    	NewGoogleDrive(ctx context.Context, tok *oauth2.Token) (*GoogleDrive, error)
    }
    
  3. 接著到Service的interface.go,把OAuthLoginURL的地方換一下之後,為了要將OAuth Token存到Dynamodb中,我們得在drive Service中能調用Dynamodb,所以這邊做一個DriveServiceDynamodbI,放入要使用的方法。

    // internal\app\service\drive\interface.go
    package drive
    
    import (
    	"context"
    
    	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
    	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
    	dynamodbAdapter "github.com/onepiece010938/Line2GoogleDriveBot/internal/adapter/dynamodb"
    	"github.com/onepiece010938/Line2GoogleDriveBot/internal/adapter/google"
    	"golang.org/x/oauth2"
    )
    
    type DriveServiceGoogleOAuthI interface {
    	OAuthLoginURL(lineID string) (oauthURL string)
    	UserOAuthToken(authCode string) (*oauth2.Token, error)
    	NewGoogleDrive(ctx context.Context, tok *oauth2.Token) (*google.GoogleDrive, error)
    }
    type DriveServiceDynamodbI interface {
    	GetGoogleOAuthToken(line_userid string) (dynamodbAdapter.GoogleOAuthToken, error)
    	CreateGoogleOAuthTable() (*types.TableDescription, error)
    	AddGoogleOAuthToken(tok dynamodbAdapter.GoogleOAuthToken) error
    	TxUpdateGoogleOAuthToken(tok dynamodbAdapter.GoogleOAuthToken) (*dynamodb.TransactWriteItemsOutput, error)
    }
    
  4. 接著更新service.go,讓NewGoogleDriveService的時候能把Dynamodb放進GoogleDriveService中,讓GoogleDriveService能直接調用到Dynamodb

    // internal\app\service\drive\service.go
    package drive
    
    import "context"
    
    type GoogleDriveService struct {
    	driveServiceGoogleOA DriveServiceGoogleOAuthI
    	driveServiceDynamodb DriveServiceDynamodbI
    }
    
    type GoogleDriveServiceParam struct {
    	DriveServiceGoogleOA DriveServiceGoogleOAuthI
    	DriveServiceDynamodb DriveServiceDynamodbI
    }
    
    func NewGoogleDriveService(_ context.Context, param GoogleDriveServiceParam) *GoogleDriveService {
    	return &GoogleDriveService{
    		driveServiceGoogleOA: param.DriveServiceGoogleOA,
    		driveServiceDynamodb: param.DriveServiceDynamodb,
    	}
    }
    
  5. 最後到application.go,新增DriveServiceDynamodb: dynamodb,注入到DriveService裡面

    // internal\app\application.go
    func NewApplication(ctx context.Context, dynamodb dynamodb.DynamodbI, oauth google.GoogleOAuthI, lineBotClient *linebot.Client) *Application {
    
    	app := &Application{
    		LineBotClient: lineBotClient,
    		SampleService: serviceSample.NewSampleService(ctx, serviceSample.SampleServiceParam{
    			SampleServiceDynamodb: dynamodb,
    		}),
    		DriveService: serviceDrive.NewGoogleDriveService(ctx, serviceDrive.GoogleDriveServiceParam{
    			DriveServiceGoogleOA: oauth,
    			DriveServiceDynamodb: dynamodb,
    		}),
    	}
    	return app
    }
    
  6. 接著我們在drive service下面,建立一個login_service.go專門處理登入相關的操作。LoginURL將傳入的lineID代入state,生成對應的Redirect URL;Login會把從Redirect URL中解出的authCode和lineID,拿去取得OAuthToken後製作成dynamodb定義的格式,最後調用AddGoogleOAuthToken把製作好的token存進去。

    // internal\app\service\drive\login_service.go
    package drive
    
    import (
    	"context"
    
    	"github.com/onepiece010938/Line2GoogleDriveBot/internal/adapter/dynamodb"
    )
    
    func (dr *GoogleDriveService) Login(ctx context.Context, lineID string, authCode string) error {
    	tok, err := dr.driveServiceGoogleOA.UserOAuthToken(authCode)
    	if err != nil {
    		return err
    	}
    
    	dToken := dynamodb.GoogleOAuthToken{
    		PK:           lineID,
    		AccessToken:  tok.AccessToken,
    		TokenType:    tok.TokenType,
    		RefreshToken: tok.RefreshToken,
    		Expiry:       tok.Expiry.String(),
    	}
    
    	err = dr.driveServiceDynamodb.AddGoogleOAuthToken(dToken)
    	if err != nil {
    		return err
    	}
    
    	return nil
    }
    
    func (dr *GoogleDriveService) LoginURL(ctx context.Context, lineID string) string {
    	oauthURL := dr.driveServiceGoogleOA.OAuthLoginURL(lineID)
    	return oauthURL
    }
    
    
  7. 最後我們回到router層把callback和OAuthLogin改成調用app中對應的service

    // internal\router\api\v1\callback.go
    for _, event := range events {
    			if event.Type == linebot.EventTypeMessage {
    				switch message := event.Message.(type) {
    				case *linebot.TextMessage:
    					if message.Text == "login" {
    						lineID := event.Source.UserID
    						authURL := app.DriveService.LoginURL(ctx, lineID)
    						if _, err = app.LineBotClient.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(authURL)).Do(); err != nil {
    							log.Println(err)
    						}
    						return
    					}
    ...
    
    // internal\router\api\v1\oauth.go
    func OAuthLogin(app *app.Application) gin.HandlerFunc {
    	return func(c *gin.Context) {
    		authCode := c.Query("code")
    		lineID := c.Query("state") //get lineid
    		err := app.DriveService.Login(c.Request.Context(), lineID, authCode)
    		if err != nil {
    			log.Printf("Unable to retrieve DriveService.Login %v", err)
    			c.String(http.StatusInternalServerError, "Unable to retrieve DriveService.Login")
    			return
    		}
    		_, err = c.Writer.Write([]byte("<html><title>Login</title> <body> Authorized successfully, please close this window</body></html>"))
    		if err != nil {
    			log.Printf("Unable to write HTML: %v", err)
    		}
    	}
    }
    
  8. 登入後打開Dynamodb admin,就可以看到成功存進去囉~

    https://ithelp.ithome.com.tw/upload/images/20231005/201159902ZhLCZejSn.png
    https://ithelp.ithome.com.tw/upload/images/20231005/20115990xIKzbdakZ7.png

那今天我們就先寫到這邊,明天再見囉~


上一篇
Day19 調整SSM+補用testify測試
下一篇
Day21 Drive Service - 取出OAuth Token操作Drive
系列文
Golang LineBot X GoogleDrive:LINE有各種限制!? 那就丟上Drive吧!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言